#pragma once
#include <iostream>
#include <memory>
#include <unordered_map>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "NoCopy.hpp"
#include "Connection.hpp"
#include "Common.hpp"
using std::cout;
using std::endl;
class TcpServer;
const static uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
const static uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
using handler_t = std::function<std::string(std::string&)>;

class TcpServer : public NoCopy, public std::enable_shared_from_this<TcpServer>
{
public:
    const static int maxevents = 64;
    const static int ipbufsize = 64;
    const static int buffersize = 1024;

    TcpServer(uint16_t port, func_t handler_message, handler_t handler)
        :_listensock_ptr(new Sock()),
        _port(port),
        _epoller_ptr(new Epoller(-1)),
        _quit(true),
        _handler_func(handler_message),
        _handler(handler)
    {}
    void Accepter(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();
        int sock = connection->_sock;
        cout << "listensock: " << sock << endl;
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int n = accept(sock, (struct sockaddr*)&peer, &len);
            if(n >= 0)
            {
                char ipbuf[ipbufsize];
                std::string peerip;
                uint16_t peerport;
                memset(ipbuf, 0, sizeof(ipbuf));
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                peerip = ipbuf;
                peerport = ntohs(peer.sin_port);
                lg(Info, "get a new link, ip: %s, port: %d, sock: %d", ipbuf, peerport, n);
                SetNonBlock(n);//设置非阻塞
                AddConnection(n, std::bind(&TcpServer::Recver, this, std::placeholders::_1), \
                    std::bind(&TcpServer::Sender, this, std::placeholders::_1), \
                    std::bind(&TcpServer::Excepter, this, std::placeholders::_1), peerip, peerport);
            }
            else
            {
                if(errno == EAGAIN) break;
                if(errno == EINTR) continue;
                else 
                {
                    lg(Error, "accept err, errstring: %s", strerror(errno));
                    connection->_except(conn);
                    break;
                }
            }
            
        }
    }

    void Recver(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();
        while(1)
        {
            char buffer[buffersize];
            ssize_t n = recv(connection->_sock, buffer, sizeof(buffer) - 1, 0);
            if(n > 0)
            {
                buffer[n] = 0;
                connection->_inbuffer += buffer;
            }
            else if(n == 0)
            {
                lg(Info, "client quit, me too...");
                if(connection->_except)
                    connection->_except(connection);
                return;
            }
            else 
            {
                if(errno == EAGAIN)
                {
                    break;
                }
                if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg(Warning, "recv err, errstring: %s", strerror(errno));
                    if(connection->_except)
                        connection->_except(connection);
                    return;
                }
            }
        }
        // lg(Debug, "inbuffer: %s", connection->_inbuffer.c_str());
        //处理请求
        _handler_func(conn);
    }

    void Sender(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();
        auto& outbuffer = connection->_outbuffer;
        while(true)
        {
            int n = send(connection->_sock, outbuffer.c_str(), outbuffer.size(), 0);
            if(n > 0)
            {
                outbuffer.erase(0, n);
                if(outbuffer.empty())
                {
                    break;
                }
            }
            else if(n == 0)
            {
                lg(Warning, "recv err, errstring: %s", strerror(errno));
                if(connection->_except)
                    connection->_except(connection);
                return;
            }
            else 
            {
                if(errno == EAGAIN) break;
                if(errno == EINTR) continue;
                else
                {
                    lg(Warning, "send err, errstring: %s", strerror(errno));
                    if(connection->_except)
                        connection->_except(connection);
                    return;
                }

            }
        }
        //输出缓冲区处理完了
        if(outbuffer.empty()) 
        {
            //只关心读
            EnableReadWrite(connection->_sock, true, false);
        }
        //输出缓冲区没有处理完
        else 
        {
            //关心读写
            EnableReadWrite(connection->_sock, true, true);
        }
    }

    void Excepter(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();

        //1.取消对对应的sock的关心
        _epoller_ptr->EpollCTL(EPOLL_CTL_DEL, connection->_sock, 0);

        //2.关闭文件描述符
        close(connection->_sock);

        //3.从unordered_map中删掉
        _connections.erase(connection->_sock);
    }
    void EnableReadWrite(int sock, bool read, bool write)
    {
        uint32_t event = (read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0) | EPOLLET;
        _epoller_ptr->EpollCTL(EPOLL_CTL_MOD, sock, event);

    }
    void AddConnection(int sock, func_t recv, func_t send, func_t except, const std::string& ip = "0.0.0.0", uint16_t port = 0)
    {
        //1.创建Connection
        std::shared_ptr<Connection> connection(new Connection(sock));
        connection->_ip = ip;
        connection->_port = port;
        connection->SetHanlder(recv, send, except);
        connection->_tcpserver = shared_from_this(); //!!!!!!!!!!
        //2.添加到unordered_map中管理起来
        _connections.insert(std::make_pair(sock, connection));

        //3.添加到内核中
        _epoller_ptr->EpollCTL(EPOLL_CTL_ADD, sock, EVENT_IN);
    }
    void Init()
    {
        _listensock_ptr->Socket();
        SetNonBlock(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        //把listensock添加入内核
        AddConnection(_listensock_ptr->Fd(), std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        lg(Info, "tcpserver init success");
    }
    void Dispatch(struct epoll_event events[], int n)
    {   
        for (size_t i = 0; i < n; i++)
        {
            int sock = events[i].data.fd;
            uint32_t event = events[i].events;
            lg(Debug, "get a event success, sock: %d", sock);

            //把异常问题交给读写
            if(event & EPOLLERR)
            {
                event |= (EPOLLIN | EPOLLOUT);
            }
            if(event & EPOLLHUP)
            {
                event |= (EPOLLIN | EPOLLOUT);
            }
            if((event & EPOLLIN) && IsConnctionValid(sock))
            {
                if(_connections[sock]->_recv)
                {
                    _connections[sock]->_recv(_connections[sock]);
                }
            }
            if((event & EPOLLOUT) && IsConnctionValid(sock))
            {
                if(_connections[sock]->_send)
                {
                    _connections[sock]->_send(_connections[sock]);
                }
            }
        }
    }
    void Loop()
    {
        _quit = false;
        struct epoll_event events[maxevents];
        memset(events, 0, sizeof(events));
        while(!_quit)
        {
            int n = _epoller_ptr->EpollWait(events, maxevents);
            Dispatch(events, n);
            PrintConnection();
        }
        _quit = true;

    }

    bool IsConnctionValid(int sock)
    {
        auto pos = _connections.find(sock);
        if(pos == _connections.end())
        {
            return false;
        }
        return true;
    }

    //DEBUG  
    void PrintConnection()
    {
        cout << "List: ";
        for(auto & e : _connections)
        {
            cout << e.first << " ";
        }
        cout << endl;
    }
private:
    std::shared_ptr<Sock> _listensock_ptr; 
    uint16_t _port;

    std::shared_ptr<Epoller> _epoller_ptr; //内核
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    bool _quit;
    func_t _handler_func;
public:
    handler_t _handler; //业务
};